package cn.wjssl.infrastructure.persistent.repository;

import cn.bugstack.middleware.db.router.strategy.IDBRouterStrategy;
import cn.wjssl.domain.award.model.aggregate.GiveOutPrizesAggregate;
import cn.wjssl.domain.award.model.aggregate.UserAwardRecordAggregate;
import cn.wjssl.domain.award.model.entity.TaskEntity;
import cn.wjssl.domain.award.model.entity.UserAwardRecordEntity;
import cn.wjssl.domain.award.model.entity.UserCreditAwardEntity;
import cn.wjssl.domain.award.model.valobj.AccountStatusVO;
import cn.wjssl.domain.award.repository.IAwardRepository;
import cn.wjssl.infrastructure.event.EventPublisher;
import cn.wjssl.infrastructure.persistent.dao.*;
import cn.wjssl.infrastructure.persistent.po.TaskPO;
import cn.wjssl.infrastructure.persistent.po.UserAwardRecordPO;
import cn.wjssl.infrastructure.persistent.po.UserCreditAccountPO;
import cn.wjssl.infrastructure.persistent.po.UserRaffleOrderPO;
import cn.wjssl.infrastructure.persistent.redis.IRedisService;
import cn.wjssl.types.common.Constants;
import cn.wjssl.types.enums.ResponseCode;
import cn.wjssl.types.exception.AppException;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * @Author：lxd
 * @Description 奖品仓储服务
 * @Date：2025/2/25
 */

@Slf4j
@Repository
public class AwardRepository implements IAwardRepository {

    @Resource
    private IUserAwardRecordDao recordDao;
    @Resource
    private ITaskDao taskDao;
    @Resource
    private IUserRaffleOrderDao userOrderDao;
    @Resource
    private IAwardDao awardDao;
    @Resource
    private IUserCreditAccountDao creditAccountDao;

    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private IDBRouterStrategy dbRouter;
    @Resource
    private EventPublisher eventPublisher;
    @Resource
    private IRedisService redisService;

    /**
     * 存储中奖记录, 并推入mq, 更新抽奖单为used
     * @param userAwardRecordAggregate
     */
    @Override
    public void saveUserAwardRecord(UserAwardRecordAggregate userAwardRecordAggregate) {
        // 获取聚合对象中的实体对象
        UserAwardRecordEntity userAwardRecordEntity = userAwardRecordAggregate.getUserAwardRecordEntity();
        TaskEntity taskEntity = userAwardRecordAggregate.getTaskEntity();
        String userId = userAwardRecordEntity.getUserId();
        Long activityId = userAwardRecordEntity.getActivityId();
        Integer awardId = userAwardRecordEntity.getAwardId();

        // 构建taskPO
        TaskPO taskPO = new TaskPO();
        taskPO.setUserId(taskEntity.getUserId());
        taskPO.setTopic(taskEntity.getTopic());
        taskPO.setMessageId(taskEntity.getMessageId());
        taskPO.setMessage(JSON.toJSONString(taskEntity.getMessage()));
        taskPO.setState(taskEntity.getState().getCode());   // 此处还是created

        // 构建recordPO
        UserAwardRecordPO recordPO = new UserAwardRecordPO();
        recordPO.setUserId(userAwardRecordEntity.getUserId());
        recordPO.setActivityId(userAwardRecordEntity.getActivityId());
        recordPO.setStrategyId(userAwardRecordEntity.getStrategyId());
        recordPO.setOrderId(userAwardRecordEntity.getOrderId());
        recordPO.setAwardId(userAwardRecordEntity.getAwardId());
        recordPO.setAwardTitle(userAwardRecordEntity.getAwardTitle());
        recordPO.setAwardTime(userAwardRecordEntity.getAwardTime());
        recordPO.setAwardState(userAwardRecordEntity.getAwardState().getCode());

        // 构建抽奖单实体
        UserRaffleOrderPO userOrderPO = new UserRaffleOrderPO();
        userOrderPO.setUserId(userAwardRecordEntity.getUserId());
        userOrderPO.setOrderId(userAwardRecordEntity.getOrderId());

        // 事务, 插入record和task
        try {
            dbRouter.doRouter(userId);
            // 事务
            transactionTemplate.execute(status -> {
                try {
                    // 写入记录
                    recordDao.insert(recordPO);
                    // 写入任务
                    taskDao.insert(taskPO);
                    // 更新抽奖单为used, 防止用同一个抽奖单进行抽奖
                    int i = userOrderDao.updateUserRaffleOrderStateUsed(userOrderPO);
                    // 如果没有更新成功, 那说明订单已经被使用过了
                    if (i != 1) {
                        status.setRollbackOnly();
                        log.error("写入中奖记录，用户抽奖单已使用过，不可重复抽奖 userId: {} activityId: {} awardId: {}", userId, activityId, awardId);
                        throw new AppException(ResponseCode.ACTIVITY_ORDER_ERROR.getCode(), ResponseCode.ACTIVITY_ORDER_ERROR.getInfo());
                    }
                    return 1;
                } catch (DuplicateKeyException e) {
                    status.setRollbackOnly();
                    log.error("写入中奖记录，唯一索引冲突 userId: {} activityId: {} awardId: {}", userId, activityId, awardId, e);
                    throw new AppException(ResponseCode.INDEX_DUP.getCode(), e);
                }
            });
        }finally {
            dbRouter.clear();
        }

        // 发送MQ消息, 发送失败捕获异常
        try {
            // 推送消息
            eventPublisher.publish(taskPO.getTopic(), taskPO.getMessage());
            // 发送成功, 就把task的状态变成completed
            taskDao.updateTaskSendMessageCompleted(taskPO);
        } catch (Exception e) {
            // 如果mq发送失败, 就将task的状态变成 fail
            log.error("写入中奖记录，发送MQ消息失败 userId: {} topic: {}", userId, taskPO.getTopic());
            taskDao.updateTaskSendMessageFail(taskPO);
        }

    }

    // 查询奖品配置
    @Override
    public String queryAwardConfig(Integer awardId) {
        return awardDao.queryAwardConfig(awardId);
    }

    /**
     * 更新中奖记录, 用户积分账户
     * 对 userId, orderId 加锁, 保证幂等性, 防止重复下单, 增加积分
     * @param giveOutPrizesAggregate
     */
    @Override
    public void saveGiveOutPrizesAggregate(GiveOutPrizesAggregate giveOutPrizesAggregate) {
        String userId = giveOutPrizesAggregate.getUserId();
        UserCreditAwardEntity userCreditEntity = giveOutPrizesAggregate.getUserCreditAwardEntity();
        UserAwardRecordEntity awardRecordEntity = giveOutPrizesAggregate.getUserAwardRecordEntity();

        // 中奖记录PO
        UserAwardRecordPO recordPO = new UserAwardRecordPO();
        recordPO.setUserId(userId);
        recordPO.setActivityId(awardRecordEntity.getActivityId());
        recordPO.setOrderId(awardRecordEntity.getOrderId());

        // 用户积分账户PO, 首次则创建
        UserCreditAccountPO creditAccountPO = new UserCreditAccountPO();
        creditAccountPO.setUserId(userId);
        creditAccountPO.setTotalAmount(userCreditEntity.getCreditAmount());
        creditAccountPO.setAvailableAmount(userCreditEntity.getCreditAmount());
        creditAccountPO.setAccountStatus(AccountStatusVO.open.getCode());

        RLock lock = redisService.getLock(Constants.RedisKey.USER_CREDIT_ACCOUNT_LOCK
                + userId
                + Constants.UNDERLINE
                + awardRecordEntity.getOrderId()
        );
        try {
            lock.lock(3, TimeUnit.SECONDS);
            dbRouter.doRouter(userId);
            transactionTemplate.execute(status -> {
                try {
                    // 更新账户积分
                    UserCreditAccountPO creditAccountPORes = creditAccountDao.queryUserCreditAccount(creditAccountPO);
                    if (creditAccountPORes == null) {   // 不存在积分账户, 新增, 不然就更新
                        creditAccountDao.insert(creditAccountPO);
                    } else {
                        creditAccountDao.updateAddAmount(creditAccountPO);
                    }

                    // 更新中奖记录
                    int i = recordDao.updateAwardRecordCompletedState(recordPO);
                    if (0 == i) {   // 更新失败, 回滚
                        log.warn("更新中奖记录，重复更新拦截 userId:{} giveOutPrizesAggregate:{}", userId, JSON.toJSONString(giveOutPrizesAggregate));
                        status.setRollbackOnly();
                    }
                    return 1;
                } catch (DuplicateKeyException e) {
                    status.setRollbackOnly();
                    log.error("更新中奖记录，唯一索引冲突 userId: {} ", userId, e);
                    throw new AppException(ResponseCode.INDEX_DUP.getCode(), e);
                }
            });
        }finally {
            dbRouter.clear();
            lock.unlock();
        }
    }

    /**
     * 根据awardId, 查询对应的award_key, 奖品类型
     * @param awardId
     * @return
     */
    @Override
    public String queryAwardKey(Integer awardId) {
        return awardDao.queryAwardKey(awardId);
    }
}
